using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.IO;
using System.Windows.Forms;
using igp.net.DataModel;

namespace igp.net.DataModel
{
    /// <summary>
    /// Determines which view range is being edited.
    /// </summary>
    public enum IgpViewEditRanges
    {
        /// <summary>
        /// None
        /// </summary>
        None,
        /// <summary>
        /// Top edge
        /// </summary>
        Top,
        /// <summary>
        /// Bottom edge
        /// </summary>
        Bottom,
        /// <summary>
        /// Left edge
        /// </summary>
        Left,
        /// <summary>
        /// Right edge
        /// </summary>
        Right
    }

    /// <summary>
    /// Represents the view object associated with the project.
    /// </summary>
    public class IgpView : IgpItem
    {
        private double DistanceX, DistanceY;
        private double gridX;
        private double gridY;
        private int height;
        private double labelX;
        private double labelY;
        private double lmaxxo, lmaxyo;
        private double lminxo, lminyo;
        private double LogDistanceX, LogDistanceY;
        private double maxxo, maxyo;
        private double minxo, minyo;
        private int vHeight;
        private int vWidth;
        private int width;

        /// <summary>
        /// Default constructor.
        /// </summary>
        public IgpView()
        {
            Axes = new IgpAxes();
            Legend = new IgpLegend();
            EditMode = IgpViewEditRanges.None;

            Defaults();
        }

        /// <summary>
        /// Sets the X scale to linear (false) or logarythmic (true).
        /// </summary>
        public bool LogScaleX { get; set; }

        /// <summary>
        /// Sets the Y scale to linear (false) or logarythmic (true).
        /// </summary>
        public bool LogScaleY { get; set; }

        /// <summary>
        /// View range minimum X coordinate.
        /// </summary>
        public double MinX { get; set; }

        /// <summary>
        /// View range minimum Y coordinate.
        /// </summary>
        public double MinY { get; set; }

        /// <summary>
        /// View range maximum X coordinate.
        /// </summary>
        public double MaxX { get; set; }

        /// <summary>
        /// View range maximym Y coordinate.
        /// </summary>
        public double MaxY { get; set; }

        /// <summary>
        /// View range minimum X coordinate for logarythmic x scale.
        /// </summary>
        public double LogMinX { get; set; }

        /// <summary>
        /// View range minimum Y coordinate for logarythmic y scale.
        /// </summary>
        public double LogMinY { get; set; }

        /// <summary>
        /// View range maximum X coordinate for logarythmic x scale.
        /// </summary>
        public double LogMaxX { get; set; }

        /// <summary>
        /// View range maximym Y coordinate for logarythmic y scale.
        /// </summary>
        public double LogMaxY { get; set; }

        /// <summary>
        /// Represents the distance in the view coordinates that corresponds to a single pixel distance on the screen.
        /// This value is for X coordinates and linear scale.
        /// </summary>
        public double VDistanceX { get; set; }

        /// <summary>
        /// Represents the distance in the view coordinates that corresponds to a single pixel distance on the screen.
        /// This value is for Y coordinates and linear scale.
        /// </summary>
        public double VDistanceY { get; set; }

        /// <summary>
        /// Represents the distance in the view coordinates that corresponds to a single pixel distance on the screen.
        /// This value is for X coordinates and logarythmic scale.
        /// </summary>
        public double VLogDistanceX { get; set; }

        /// <summary>
        /// Represents the distance in the view coordinates that corresponds to a single pixel distance on the screen.
        /// This value is for Y coordinates and logarythmic scale.
        /// </summary>
        public double VLogDistanceY { get; set; }

        /// <summary>
        /// Gets the axes object of the view.
        /// </summary>
        public IgpAxes Axes { get; private set; }

        /// <summary>
        /// Gives access to the legend.
        /// </summary>
        public IgpLegend Legend { get; private set; }

        /// <summary>
        /// Represents width of the image that is rendered in memory.
        /// </summary>
        public int Width
        {
            get { return width; }
            set
            {
                if (value < 160) throw new ArgumentException("Width cannot be smaller than 160");
                width = value;
            }
        }

        /// <summary>
        /// Represents height of the image that is rendered in memory.
        /// </summary>
        public int Height
        {
            get { return height; }
            set
            {
                if (value < 120) throw new ArgumentException("Width cannot be smaller than 120");
                height = value;
            }
        }

        /// <summary>
        /// Represents width of the image that is drawn on the screen.
        /// </summary>
        public int VWidth
        {
            get { return vWidth; }
            set
            {
                if (value < 160) throw new ArgumentException("Width cannot be smaller than 160");
                vWidth = value;
            }
        }

        /// <summary>
        /// Represents height of the image that is drawn on the screen.
        /// </summary>
        public int VHeight
        {
            get { return vHeight; }
            set
            {
                if (value < 120) throw new ArgumentException("Width cannot be smaller than 160");
                vHeight = value;
            }
        }

        /// <summary>
        /// Gets/Sets the background color.
        /// </summary>
        public Color BackgroundColor { get; set; }

        /// <summary>
        /// Gets/Sets the grid color.
        /// </summary>
        public Color GridColor { get; set; }

        /// <summary>
        /// Enables/Disables the X grid.
        /// </summary>
        public bool DrawXGrid { get; set; }

        /// <summary>
        /// Enables/Diables the Y grid.
        /// </summary>
        public bool DrawYGrid { get; set; }

        /// <summary>
        /// Enables/Disables the X labels.
        /// </summary>
        public bool DrawXLabels { get; set; }

        /// <summary>
        /// Enables/Disables the Y labels.
        /// </summary>
        public bool DrawYLabels { get; set; }

        /// <summary>
        /// Gets/Sets size of the X grid - distance between two grid lines.
        /// </summary>
        public double GridX
        {
            get { return gridX; }
            set
            {
                if (value == 0) throw new ArgumentException("Grid size cannot be 0!");
                gridX = value;
            }
        }

        /// <summary>
        /// Gets/Sets size of the Y grid - distance between two grid lines.
        /// </summary>
        public double GridY
        {
            get { return gridY; }
            set
            {
                if (value == 0) throw new ArgumentException("Grid size cannot be 0!");
                gridY = value;
            }
        }

        /// <summary>
        /// Gets/Sets the distance between two x axis labels.
        /// </summary>
        public double LabelX
        {
            get { return labelX; }
            set
            {
                if (value == 0) throw new ArgumentException("Labels' distance cannot be 0!");
                labelX = value;
            }
        }

        /// <summary>
        /// Gets/Sets the distance between two y axis labels.
        /// </summary>
        public double LabelY
        {
            get { return labelY; }
            set
            {
                if (value == 0) throw new ArgumentException("Labels' distance cannot be 0!");
                labelY = value;
            }
        }

        /// <summary>
        /// If true the drawing fill be fitted to the dimensions of the view window.
        /// Dimensions of both screen image, and memory rendered image will be adjusted automatically.
        /// </summary>
        public bool FitDraw { get; set; }

        /// <summary>
        /// Gets/Sets the user interaction mode.
        /// </summary>
        public IgpViewEditRanges EditMode { get; set; }

        /// <summary>
        /// Draws the view on the screen.
        /// </summary>
        /// <param name="graph">Graphics object used for drawing</param>
        public override void Draw(Graphics graph)
        {
            //Draw background
            graph.Clear(BackgroundColor);

            //Draw Grid

            var beginx = (int) (-(MinX - ((int) (MinX/GridX))*GridX)*DistanceX);
            var beginy = (int) (-(MinY - ((int) (MinY/GridY))*GridY)*DistanceY);

            var beginxl = (int) (-(MinX - ((int) (MinX/LabelX))*LabelX)*DistanceX);
            double beginxlr = ((int) (MinX/LabelX))*LabelX;
            var beginyl = (int) (-(MinY - ((int) (MinY/LabelY))*LabelY)*DistanceY);
            double beginylr = ((int) (MinY/LabelY))*LabelY;

            var gridpen = new Pen(GridColor) {DashStyle = LineType};

            //Y Grid - not logarythmic
            if (DrawYGrid && !LogScaleY)
            {
                for (int a = 0; a <= (MaxY - MinY)/GridY + 1; a++)
                {
                    int c = Height - beginy - (int) (a*DistanceY*GridY);
                    graph.DrawLine(gridpen, 0, c, Width, c);
                }
            }

            //X Grid - not logarythmic

            if (DrawXGrid && !LogScaleX)
            {
                for (int a = 0; a <= (MaxX - MinX)/GridX + 1; a++)
                {
                    int c = beginx + (int) (a*DistanceX*GridX);
                    graph.DrawLine(gridpen, c, 0, c, Height);
                }
            }

            //Y Grid - logarythmic

            if (DrawYGrid && LogScaleY)
            {
                double step = Math.Pow(10, (int) (10*Math.Log10(LogMinY)/10) - 1);
                double now = step;
                
                while (now < LogMaxY)
                {
                    float d = GetScreenCoordinateY(now);
                    graph.DrawLine(gridpen, 0, d, Width, d);
                    now += step;
                    double a = Math.Round(Math.Log10(now), 12);
                    var b = (int) a;
                    if (a == b) step *= 10;
                }
            }

            //X Grid - logarythmic
            if (DrawXGrid && LogScaleX)
            {
                double step = Math.Pow(10, (int) (10*Math.Log10(LogMinX)/10) - 1);
                double now = step;
                
                while (now < LogMaxX)
                {
                    float d = GetScreenCoordinateX(now);
                    graph.DrawLine(gridpen, d, 0, d, Height);
                    now += step;
                    double a = Math.Round(Math.Log10(now), 12);
                    var b = (int) a;
                    if (a == b) step *= 10;
                }
            }

            Axes.Draw(graph);

            //Draw labels

            //Y - logarythmic
            if (DrawYLabels && LogScaleY)
            {
                double step = Math.Pow(10, (int) (10*Math.Log10(LogMinY)/10) - 1);
                double now = step;
                
                while (now < LogMaxY)
                {
                    float d = GetScreenCoordinateY(now);
                
                    if (Math.Round(Math.Log10(now), 4) == (int) Math.Log10(now))
                    {
                        if (Math.Round(now, 4) != 1)
                        {
                            //graph.DrawLine(new Pen(Color.DarkGray),0,d,Width,d);
                            if (d > 5 && d < Height - 5)
                            {
                                graph.DrawString("10", igpSettings.Singleton.ChartFont, new SolidBrush(Axes.LineColor),
                                                 5, d - 5);
                                graph.DrawString(Math.Round(Math.Log10(now), 4).ToString(),
                                                 new Font("Calibri", 5), new SolidBrush(Axes.LineColor), 19, d - 8);
                            }
                        }
                        else
                        {
                            graph.FillRectangle(new SolidBrush(BackgroundColor), 3, d - 5, 12, 10);
                            graph.DrawString("1", igpSettings.Singleton.ChartFont, new SolidBrush(Axes.LineColor), 5,
                                             d - 5);
                        }
                    }
                    now += step;
                    double a = Math.Round(Math.Log10(now), 12);
                    var b = (int) a;
                    if (a == b) step *= 10;
                }
            }

            //X - logarythmic
            if (DrawXLabels && LogScaleX)
            {
                double step = Math.Pow(10, (int) (10*Math.Log10(LogMinX)/10) - 1);
                double now = step;
                while (now < LogMaxX)
                {
                    float d = GetScreenCoordinateX(now);

                    if (Math.Round(Math.Log10(now), 4) == (int) Math.Log10(now))
                    {
                        if (Math.Round(now, 4) != 1)
                        {
                            if (d > 5 && d < Width - 13)
                            {
                                graph.DrawString("10", igpSettings.Singleton.ChartFont, new SolidBrush(Axes.LineColor),
                                                 d - 5, Height - 12);
                                graph.DrawString(Math.Round(Math.Log10(now), 4).ToString(),
                                                 new Font("Calibri", 5), new SolidBrush(Axes.LineColor), d + 9,
                                                 Height - 15);
                            }
                        }
                        else
                        {
                            graph.FillRectangle(new SolidBrush(BackgroundColor), d - 7, Height - 12, 12, 10);
                            graph.DrawString("1", igpSettings.Singleton.ChartFont, new SolidBrush(Axes.LineColor), d - 5,
                                             Height - 12);
                        }
                    }


                    now += step;
                    double a = Math.Round(Math.Log10(now), 12);
                    var b = (int) a;
                    if (a == b) step *= 10;
                }
            }

            //Y Grid - linear
            if (DrawYLabels && !LogScaleY)
            {
                graph.FillRectangle(new SolidBrush(BackgroundColor), 3, GetScreenCoordinateY(Axes.YCoordinateLinear) - 5, 12, 10);
                
                for (int a = 0; a <= (MaxY - MinY)/LabelY + 1; a++)
                {
                    int c = Height - beginyl - (int) (a*DistanceY*LabelY);
                    String label = Math.Round(beginylr + a*LabelY, 4).ToString();
                    
                    if (c > 5 && c < Height - 5)
                        graph.DrawString(label, igpSettings.Singleton.ChartFont, new SolidBrush(Axes.LineColor), 5, c - 5);
                }
            }

            //X Grid - linear

            if (DrawXLabels && !LogScaleX)
            {
                graph.FillRectangle(new SolidBrush(BackgroundColor), GetScreenCoordinateX(Axes.XCoordinateLinear) - 5, Height - 12, 12, 10);
                
                for (int a = 0; a <= (MaxX - MinX)/LabelX + 1; a++)
                {
                    int c = beginxl + (int) (a*DistanceX*LabelX);
                    String label = Math.Round(beginxlr + a*LabelX, 4).ToString();
                    
                    if (c > 5 && c < Width - 3)
                        graph.DrawString(label, igpSettings.Singleton.ChartFont, new SolidBrush(Axes.LineColor), c - 3, Height - 12);
                }
            }
        }

        /// <summary>
        /// Gets the width ratio - (Render Image Width)/(Screen Image Width)
        /// </summary>
        /// <returns>width ratio</returns>
        public double GetRatioX()
        {
            return VWidth/(double) Width;
        }

        /// <summary>
        /// Gets the height ratio - (Render Image Height)/(Screen Image Height)
        /// </summary>
        /// <returns>height ratio</returns>
        public double GetRatioY()
        {
            return VHeight/(double) Height;
        }

        /// <summary>
        /// This method is required for item's reaction to mouse movement. 
        /// You must invoke this base method in your derived class in order 
        /// to update some useful properties.
        /// </summary>
        /// <param name="graph">Graphics object that can be used to paint something on the screen</param>
        /// <param name="e">Mouse Event Details</param>
        public override void InteractMove(Graphics graph, MouseEventArgs e)
        {
            //if (Edited)
            base.InteractMove(graph, e);
            if (e.Button == MouseButtons.Right)
            {
                //Update View Ranges
                double DoubleVectorX = !LogScaleX ? VectorX/VDistanceX : VectorX/VLogDistanceX;
                double DoubleVectorY = !LogScaleY ? VectorY/VDistanceY : VectorY/VLogDistanceY;

                if (!LogScaleX)
                {
                    MinX = minxo + DoubleVectorX;
                    MaxX = maxxo + DoubleVectorX;
                }
                else
                {
                    LogMinX = Math.Pow(10, (Math.Log10(lminxo) + DoubleVectorX/10));
                    LogMaxX = Math.Pow(10, (Math.Log10(lmaxxo) + DoubleVectorX/10));
                }

                if (!LogScaleY)
                {
                    MinY = minyo - DoubleVectorY;
                    MaxY = maxyo - DoubleVectorY;
                }
                else
                {
                    LogMinY = Math.Pow(10, (Math.Log10(lminyo) - DoubleVectorY/10));
                    LogMaxY = Math.Pow(10, (Math.Log10(lmaxyo) - DoubleVectorY/10));
                }

                DrawViewRanges(graph);
                FormMain.Singleton.EditorWindow.ReadRanges();
            }

            if (e.Button == MouseButtons.Middle)
            {
                //Update View Ranges
                double DoubleVectorX = !LogScaleX ? VectorX/VDistanceX : VectorX/VLogDistanceX;
                double DoubleVectorY = !LogScaleY ? VectorY/VDistanceY : VectorY/VLogDistanceY;

                if (!LogScaleX)
                {
                    MinX = minxo - DoubleVectorX;
                    MaxX = maxxo + DoubleVectorX;
                }
                else
                {
                    LogMinX = Math.Pow(10, (Math.Log10(lminxo) - DoubleVectorX/10));
                    LogMaxX = Math.Pow(10, (Math.Log10(lmaxxo) + DoubleVectorX/10));
                }

                if (!LogScaleY)
                {
                    MinY = minyo + DoubleVectorY;
                    MaxY = maxyo - DoubleVectorY;
                }
                else
                {
                    LogMinY = Math.Pow(10, (Math.Log10(lminyo) + DoubleVectorY/10));
                    LogMaxY = Math.Pow(10, (Math.Log10(lmaxyo) - DoubleVectorY/10));
                }

                DrawViewRanges(graph);

                if (DistanceY*GridY < 10)
                {
                    FormMain.Singleton.LogMessage("warning: Y grid and labels are too dense. ");
                    GridY *= 5;
                    LabelY *= 5;
                    FormMain.Singleton.EditorWindow.ReadViewProperities();
                }


                if (DistanceX*GridX < 10)
                {
                    FormMain.Singleton.LogMessage("warning: X grid and labels are too dense. ");
                    GridX *= 5;
                    LabelX *= 5;
                    FormMain.Singleton.EditorWindow.ReadViewProperities();
                }

                FormMain.Singleton.EditorWindow.ReadRanges();
            }

            if (FormMain.Singleton.EditorWindow.GetActivePage() == 0 && e.Button == MouseButtons.None)
            {
                if (e.Y > 0 && e.Y < 10 && e.X > 10 && e.X < IgpProject.Singleton.View.Width - 10)
                {
                    DrawMessage(graph, "Drag to change maximum y", new Point(IgpProject.Singleton.View.Width/2, 5), 1);
                }

                if (e.Y > IgpProject.Singleton.View.Height - 10 && e.Y < IgpProject.Singleton.View.Height && e.X > 10 &&
                    e.X < IgpProject.Singleton.View.Width - 10)
                {
                    DrawMessage(graph, "Drag to change minimum y",
                                new Point(IgpProject.Singleton.View.Width/2,
                                          (int)
                                          (IgpProject.Singleton.View.Height - (8 + igpSettings.Singleton.ChartFont.Size))),
                                1);
                }

                if (e.Y > 10 && e.Y < IgpProject.Singleton.View.Height - 10 && e.X > 0 && e.X < 10)
                {
                    DrawMessage(graph, "Drag to change minimum x", new Point(5, IgpProject.Singleton.View.Height/2 + 5),
                                0);
                }

                if (e.Y > 10 && e.Y < IgpProject.Singleton.View.Height - 10 &&
                    e.X > IgpProject.Singleton.View.Width - 10 && e.X < IgpProject.Singleton.View.Width)
                {
                    DrawMessage(graph, "Drag to change maximum x",
                                new Point(IgpProject.Singleton.View.Width - 5, IgpProject.Singleton.View.Height/2 + 5),
                                2);
                }
            }

            if (e.Button == MouseButtons.Left && FormMain.Singleton.EditorWindow.GetActivePage() == 0)
            {
                //Update View Ranges
                double DoubleVectorX = !LogScaleX ? VectorX/VDistanceX : VectorX/VLogDistanceX;
                double DoubleVectorY = !LogScaleY ? VectorY/VDistanceY : VectorY/VLogDistanceY;

                if (EditMode == IgpViewEditRanges.Top)
                {
                    if (!LogScaleY)
                    {
                        MaxY = maxyo - DoubleVectorY;
                    }
                    else
                    {
                        LogMaxY = Math.Pow(10, (Math.Log10(lmaxyo) - DoubleVectorY/10));
                    }
                }

                if (EditMode == IgpViewEditRanges.Bottom)
                {
                    if (!LogScaleY)
                    {
                        MinY = minyo - DoubleVectorY;
                    }
                    else
                    {
                        LogMinY = Math.Pow(10, (Math.Log10(lminyo) - DoubleVectorY/10));
                    }
                }

                if (EditMode == IgpViewEditRanges.Left)
                {
                    if (!LogScaleX)
                    {
                        MinX = minxo + DoubleVectorX;
                    }
                    else
                    {
                        LogMinX = Math.Pow(10, (Math.Log10(lminxo) + DoubleVectorX/10));
                    }
                }

                if (EditMode == IgpViewEditRanges.Right)
                {
                    if (!LogScaleX)
                    {
                        MaxX = maxxo + DoubleVectorX;
                    }
                    else
                    {
                        LogMaxX = Math.Pow(10, (Math.Log10(lmaxxo) + DoubleVectorX/10));
                    }
                }

                if (DistanceY*GridY < 10)
                {
                    FormMain.Singleton.LogMessage("warning: Y grid and labels are too dense. ");
                    GridY *= 5;
                    LabelY *= 5;
                    FormMain.Singleton.EditorWindow.ReadViewProperities();
                }


                if (DistanceX*GridX < 10)
                {
                    FormMain.Singleton.LogMessage("warning: X grid and labels are too dense. ");
                    GridX *= 5;
                    LabelX *= 5;
                    FormMain.Singleton.EditorWindow.ReadViewProperities();
                }

                DrawViewRanges(graph);
                FormMain.Singleton.EditorWindow.ReadRanges();
            }

            if (FormMain.Singleton.EditorWindow.GetActivePage() != 0) return;

            Axes.InteractMove(graph, e);
            Legend.InteractMove(graph, e);
        }

        /// <summary>
        /// Draws a message on the screen.
        /// </summary>
        /// <param name="graph">Graphics object used for drawing</param>
        /// <param name="str">String to be drawn</param>
        /// <param name="pt">Coordinates of the string</param>
        /// <param name="align">Text alignment: 0 - left, 1 - center, 2 - right</param>
        public void DrawMessage(Graphics graph, string str, Point pt, int align)
        {
            //Present current values to the user
            Font ft = igpSettings.Singleton.ChartFont;
            var sb = new SolidBrush(Color.Black);

            int widh = graph.MeasureString(str, ft).ToSize().Width + 4;

            if (align == 1) pt.X -= widh/2;
            if (align == 2) pt.X -= widh;

            var re1 = new Rectangle(pt, new Size(widh, (int) (igpSettings.Singleton.ChartFont.Size + 6)));
            var res1 = new Rectangle(re1.X + 3, re1.Y + 3, re1.Width, re1.Height);

            graph.FillRectangle(new SolidBrush(Color.FromArgb(100, 0, 0, 0)), res1);
            graph.FillRectangle(new LinearGradientBrush(re1, Color.White, Color.FromArgb(230, 230, 230), 0, false), re1);
            graph.DrawRectangle(new Pen(Color.Black), re1);
            graph.DrawString(str, ft, sb, pt);
        }

        private void DrawViewRanges(Graphics graph)
        {
            //Present current values to the user
            Font ft = igpSettings.Singleton.ChartFont;
            var sb = new SolidBrush(Color.Black);

            int widh1;
            int widv1;
            int widh2;
            int widv2;

            if (!LogScaleX)
            {
                widh1 = graph.MeasureString(MinX.ToString("0.0000"), ft).ToSize().Width;
                widh2 = graph.MeasureString(MaxX.ToString("0.0000"), ft).ToSize().Width;
            }
            else
            {
                widh1 = graph.MeasureString(LogMinX.ToString("0.0000"), ft).ToSize().Width;
                widh2 = graph.MeasureString(LogMaxX.ToString("0.0000"), ft).ToSize().Width;
            }

            if (!LogScaleY)
            {
                widv1 = graph.MeasureString(MaxY.ToString("0.0000"), ft).ToSize().Width;
                widv2 = graph.MeasureString(MinY.ToString("0.0000"), ft).ToSize().Width;
            }
            else
            {
                widv1 = graph.MeasureString(LogMaxY.ToString("0.0000"), ft).ToSize().Width;
                widv2 = graph.MeasureString(LogMinY.ToString("0.0000"), ft).ToSize().Width;
            }

            var re1 = new Rectangle(5, Height/2 - 5, widh1, (int) (igpSettings.Singleton.ChartFont.Size + 5));
            var re2 = new Rectangle(Width - widh2 - 5, Height/2 - 5, widh2, (int) (igpSettings.Singleton.ChartFont.Size + 5));
            var re3 = new Rectangle(Width/2 - widv1/2, 5, widv1, (int) (igpSettings.Singleton.ChartFont.Size + 5));
            var re4 = new Rectangle(Width/2 - widv2/2, Height - 15, widv2, (int) (igpSettings.Singleton.ChartFont.Size + 5));

            var res1 = new Rectangle(re1.X + 3, re1.Y + 3, re1.Width, re1.Height);
            var res2 = new Rectangle(re2.X + 3, re2.Y + 3, re2.Width, re2.Height);
            var res3 = new Rectangle(re3.X + 3, re3.Y + 3, re3.Width, re3.Height);
            var res4 = new Rectangle(re4.X + 3, re4.Y + 3, re4.Width, re4.Height);

            var strokeBrush = new SolidBrush(Color.FromArgb(100, 0, 0, 0));
            graph.FillRectangle(strokeBrush, res1);
            graph.FillRectangle(strokeBrush, res2);
            graph.FillRectangle(strokeBrush, res3);
            graph.FillRectangle(strokeBrush, res4);

            Color gradientColor = Color.FromArgb(230, 230, 230);
            graph.FillRectangle(new LinearGradientBrush(re1, Color.White, gradientColor, 0, false), re1);
            graph.FillRectangle(new LinearGradientBrush(re2, Color.White, gradientColor, 0, false), re2);
            graph.FillRectangle(new LinearGradientBrush(re3, Color.White, gradientColor, 0, false), re3);
            graph.FillRectangle(new LinearGradientBrush(re4, Color.White, gradientColor, 0, false), re4);

            var blackPen = new Pen(Color.Black);
            graph.DrawRectangle(blackPen, re1);
            graph.DrawRectangle(blackPen, re2);
            graph.DrawRectangle(blackPen, re3);
            graph.DrawRectangle(blackPen, re4);


            if (!LogScaleX)
            {
                graph.DrawString(MinX.ToString("0.0000"), ft, sb, re1);
                graph.DrawString(MaxX.ToString("0.0000"), ft, sb, re2);
            }
            else
            {
                graph.DrawString(LogMinX.ToString("0.0000"), ft, sb, re1);
                graph.DrawString(LogMaxX.ToString("0.0000"), ft, sb, re2);
            }

            if (!LogScaleY)
            {
                graph.DrawString(MaxY.ToString("0.0000"), ft, sb, re3);
                graph.DrawString(MinY.ToString("0.0000"), ft, sb, re4);
            }
            else
            {
                graph.DrawString(LogMaxY.ToString("0.0000"), ft, sb, re3);
                graph.DrawString(LogMinY.ToString("0.0000"), ft, sb, re4);
            }
        }

        /// <summary>
        /// This method is required for item's reaction to mouse buttons being pressed. 
        /// You must invoke this base method in your derived class in order 
        /// to update some useful properties.
        /// </summary>
        /// <param name="graph">Graphics object that can be used to paint something on the screen</param>
        /// <param name="e">Mouse Event Details</param>
        public override void InteractDown(Graphics graph, MouseEventArgs e)
        {
            base.InteractDown(graph, e);
            maxxo = MaxX;
            minxo = MinX;
            maxyo = MaxY;
            minyo = MinY;

            lmaxxo = LogMaxX;
            lminxo = LogMinX;
            lmaxyo = LogMaxY;
            lminyo = LogMinY;

            if (e.Button == MouseButtons.Right || e.Button == MouseButtons.Middle)
            {
                InteractionInUse = true;
            }

            if (e.Button == MouseButtons.Left && FormMain.Singleton.EditorWindow.GetActivePage() == 0)
            {
                if (e.Y > 0 && e.Y < 10 && e.X > 10 && e.X < IgpProject.Singleton.View.Width - 10)
                {
                    EditMode = IgpViewEditRanges.Top;
                    InteractionInUse = true;
                }

                if (e.Y > IgpProject.Singleton.View.Height - 10 && e.Y < IgpProject.Singleton.View.Height && e.X > 10 &&
                    e.X < IgpProject.Singleton.View.Width - 10)
                {
                    EditMode = IgpViewEditRanges.Bottom;
                    InteractionInUse = true;
                }

                if (e.Y > 10 && e.Y < IgpProject.Singleton.View.Height - 10 && e.X > 0 && e.X < 10)
                {
                    EditMode = IgpViewEditRanges.Left;
                    InteractionInUse = true;
                }

                if (e.Y > 10 && e.Y < IgpProject.Singleton.View.Height - 10 &&
                    e.X > IgpProject.Singleton.View.Width - 10 && e.X < IgpProject.Singleton.View.Width)
                {
                    EditMode = IgpViewEditRanges.Right;
                    InteractionInUse = true;
                }
            }


            if (FormMain.Singleton.EditorWindow.GetActivePage() == 0)
            {
                Axes.InteractDown(graph, e);
                Legend.InteractDown(graph, e);
            }
        }

        /// <summary>
        /// This method is required for item's reaction to mouse buttons being released. 
        /// </summary>
        /// <param name="graph">Graphics object that can be used to paint something on the screen</param>
        /// <param name="e">Mouse Event Details</param>
        public override void InteractUp(Graphics graph, MouseEventArgs e)
        {
            base.InteractUp(graph, e);

            if (InteractionInUse) InteractionInUse = false;

            EditMode = IgpViewEditRanges.None;

            if (FormMain.Singleton.EditorWindow.GetActivePage() != 0) return;

            Axes.InteractUp(graph, e);
            Legend.InteractUp(graph, e);
        }

        /// <summary>
        /// Sets all of the view settings to defaults.
        /// </summary>
        public void Defaults()
        {
            Axes.Defaults();
            Legend.Defaults();

            BackgroundColor = Color.FromArgb(230, 230, 230);
            GridColor = Color.LightGray;
            Axes.LineColor = Color.Black;

            VWidth = 512;
            VHeight = 384;
            Width = 512;
            Height = 384;

            MinX = -40.0/6;
            MinY = -5;
            MaxX = 40.0/6;
            MaxY = 5;

            LogMinX = 0.1;
            LogMinY = 0.1;
            LogMaxX = 10;
            LogMaxY = 10;

            LogScaleX = false;
            LogScaleY = false;

            UpdateDistances();

            GridX = 1;
            GridY = 1;
            LabelX = 1;
            LabelY = 1;

            DrawXGrid = true;
            DrawYGrid = true;
            DrawXLabels = true;
            DrawYLabels = true;

            FitDraw = false;
            LineType = DashStyle.Solid;
        }

        /// <summary>
        /// Updates some properties after resolution changes.
        /// </summary>
        public void UpdateDistances()
        {
            DistanceX = Width/(MaxX - MinX);
            DistanceY = Height/(MaxY - MinY);

            LogDistanceX = Width/(10*Math.Log10(LogMaxX) - 10*Math.Log10(LogMinX));
            LogDistanceY = Height/(10*Math.Log10(LogMaxY) - 10*Math.Log10(LogMinY));

            VDistanceX = VWidth/(MaxX - MinX);
            VDistanceY = VHeight/(MaxY - MinY);

            VLogDistanceX = VWidth/(10*Math.Log10(LogMaxX) - 10*Math.Log10(LogMinX));
            VLogDistanceY = VHeight/(10*Math.Log10(LogMaxY) - 10*Math.Log10(LogMinY));
        }

        /// <summary>
        /// Converts the x coordinate from real coordinates to pixels (memory rendered image)
        /// </summary>
        /// <param name="x">real x coordinates</param>
        /// <returns>x coordinate in pixels</returns>
        public int GetScreenCoordinateX(double x)
        {
            if (!LogScaleX)
                return (int) ((x - MinX)*DistanceX);

            if (x > 0)
                return (int) ((10*Math.Log10(x) - 10*Math.Log10(LogMinX))*LogDistanceX);

            return -1;
        }

        /// <summary>
        /// Converts the y coordinate from real coordinates to pixels (memory rendered image)
        /// </summary>
        /// <param name="y">real y coordinate</param>
        /// <returns>y coordinate in pixels</returns>
        public int GetScreenCoordinateY(double y)
        {
            if (!LogScaleY)
                return (int) (Height - (y - MinY)*DistanceY);

            if (y > 0)
                return (int) (Height - (10*Math.Log10(y) - 10*Math.Log10(LogMinY))*LogDistanceY);

            return Height + 1;
        }

        /// <summary>
        /// Converts the x coordinate from real coordinates to pixels (image on the screen)
        /// </summary>
        /// <param name="x">real x coordinate</param>
        /// <returns>x coordinate in pixels</returns>
        public int GetVScreenCoordinateX(double x)
        {
            if (!LogScaleX)
                return (int) ((x - MinX)*VDistanceX);

            if (x > 0)
                return (int) ((10*Math.Log10(x) - 10*Math.Log10(LogMinX))*VLogDistanceX);

            return -1;
        }

        /// <summary>
        /// Converts the y coordinate from real coordinates to pixels (image on the screen)
        /// </summary>
        /// <param name="y">real y coordinate</param>
        /// <returns>y coordinate in pixels</returns>
        public int GetVScreenCoordinateY(double y)
        {
            if (!LogScaleY)
                return (int) (VHeight - (y - MinY)*VDistanceY);

            if (y > 0)
                return (int) (VHeight - (10*Math.Log10(y) - 10*Math.Log10(LogMinY))*VLogDistanceY);

            return VHeight + 1;
        }

        /// <summary>
        /// Converts the x coordinate from pixels (image on the screen) to real coordinates
        /// </summary>
        /// <param name="x">x coordinate in pixels</param>
        /// <returns>real x coordinate</returns>
        public double GetRealCoordinateX(int x)
        {
            if (LogScaleX)
                return Math.Pow(10, (x/VLogDistanceX + 10*Math.Log10(LogMinX))/10);

            return x/VDistanceX + MinX;
        }

        /// <summary>
        /// Converts the y coordinate from pixels (image on the screen) to real coordinates
        /// </summary>
        /// <param name="y">y coordinate in pixels</param>
        /// <returns>real y coordinate</returns>
        public double GetRealCoordinateY(int y)
        {
            if (LogScaleY)
                return Math.Pow(10, ((Height - y)/VLogDistanceY + 10*Math.Log10(LogMinY))/10);

            return (VHeight - y)/VDistanceY + MinY;
        }

        /// <summary>
        /// Saves data to a TextWriter.
        /// </summary>
        /// <param name="sw">data output</param>
        public override void SaveData(TextWriter sw)
        {
            sw.WriteLine("<View>");

            sw.WriteLine("linminx = {0}", MinX);
            sw.WriteLine("linmaxx = {0}", MaxX);
            sw.WriteLine("linminy = {0}", MinY);
            sw.WriteLine("linmaxy = {0}", MaxY);
            sw.WriteLine("logminx = {0}", LogMinX);
            sw.WriteLine("logmaxx = {0}", LogMaxX);
            sw.WriteLine("logminy = {0}", LogMinY);
            sw.WriteLine("logmaxy = {0}", LogMaxY);

            sw.WriteLine("logscalex = {0}", LogScaleX);
            sw.WriteLine("logscaley = {0}", LogScaleY);
            sw.WriteLine("bgcolor = {0}", BackgroundColor.ToArgb());
            sw.WriteLine("gdcolor = {0}", GridColor.ToArgb());

            sw.WriteLine("drawgridx = {0}", DrawXGrid);
            sw.WriteLine("drawgridy = {0}", DrawYGrid);
            sw.WriteLine("drawlabelsx = {0}", DrawXLabels);
            sw.WriteLine("drawlabelsy = {0}", DrawYLabels);

            sw.WriteLine("fitdraw = {0}", FitDraw);

            sw.WriteLine("gridx = {0}", GridX);
            sw.WriteLine("gridy = {0}", GridY);
            sw.WriteLine("labelx = {0}", LabelX);
            sw.WriteLine("labely = {0}", LabelY);

            sw.WriteLine("width = {0}", Width);
            sw.WriteLine("vwidth = {0}", VWidth);
            sw.WriteLine("height = {0}", Height);
            sw.WriteLine("vheight = {0}", VHeight);

            sw.WriteLine("gdtype = {0}", LineType);

            sw.WriteLine("</View>");

            Axes.SaveData(sw);
            Legend.SaveData(sw);
        }

        /// <summary>
        /// Loads data from a TextReader.
        /// </summary>
        /// <param name="sr">data input</param>
        public override void ReadData(TextReader sr)
        {
            String Line;
            while ((Line = sr.ReadLine()) != "</View>")
            {
                char[] sep = {'='};
                String[] cmd = Line.Split(sep, 2);

                if (cmd[0].StartsWith("linminx"))
                {
                    MinX = double.Parse(cmd[1]);
                }

                if (cmd[0].StartsWith("linmaxx"))
                {
                    MaxX = double.Parse(cmd[1]);
                }

                if (cmd[0].StartsWith("linminy"))
                {
                    MinY = double.Parse(cmd[1]);
                }

                if (cmd[0].StartsWith("linmaxy"))
                {
                    MaxY = double.Parse(cmd[1]);
                }

                if (cmd[0].StartsWith("logminx"))
                {
                    LogMinX = double.Parse(cmd[1]);
                }

                if (cmd[0].StartsWith("logmaxx"))
                {
                    LogMaxX = double.Parse(cmd[1]);
                }

                if (cmd[0].StartsWith("logminy"))
                {
                    LogMinY = double.Parse(cmd[1]);
                }

                if (cmd[0].StartsWith("logmaxy"))
                {
                    LogMaxY = double.Parse(cmd[1]);
                }

                if (cmd[0].StartsWith("logscalex"))
                {
                    LogScaleX = bool.Parse(cmd[1]);
                }

                if (cmd[0].StartsWith("logscaley"))
                {
                    LogScaleY = bool.Parse(cmd[1]);
                }

                if (cmd[0].StartsWith("bgcolor"))
                {
                    BackgroundColor = Color.FromArgb(int.Parse(cmd[1]));
                }

                if (cmd[0].StartsWith("gdcolor"))
                {
                    GridColor = Color.FromArgb(int.Parse(cmd[1]));
                }

                if (cmd[0].StartsWith("drawgridx"))
                {
                    DrawXGrid = bool.Parse(cmd[1]);
                }

                if (cmd[0].StartsWith("drawgridy"))
                {
                    DrawYGrid = bool.Parse(cmd[1]);
                }

                if (cmd[0].StartsWith("drawlabelsx"))
                {
                    DrawXLabels = bool.Parse(cmd[1]);
                }

                if (cmd[0].StartsWith("drawlabelsy"))
                {
                    DrawYLabels = bool.Parse(cmd[1]);
                }

                if (cmd[0].StartsWith("fitdraw"))
                {
                    FitDraw = bool.Parse(cmd[1]);
                }

                if (cmd[0].StartsWith("gridx"))
                {
                    GridX = double.Parse(cmd[1]);
                }

                if (cmd[0].StartsWith("gridy"))
                {
                    GridY = double.Parse(cmd[1]);
                }

                if (cmd[0].StartsWith("labelx"))
                {
                    LabelX = double.Parse(cmd[1]);
                }

                if (cmd[0].StartsWith("labely"))
                {
                    LabelY = double.Parse(cmd[1]);
                }

                if (cmd[0].StartsWith("width"))
                {
                    Width = int.Parse(cmd[1]);
                }

                if (cmd[0].StartsWith("height"))
                {
                    Height = int.Parse(cmd[1]);
                }

                if (cmd[0].StartsWith("vwidth"))
                {
                    VWidth = int.Parse(cmd[1]);
                }

                if (cmd[0].StartsWith("vheight"))
                {
                    VHeight = int.Parse(cmd[1]);
                }
            }
        }
    }
}